/*
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.paoding.rose.web.var;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import net.paoding.rose.web.Invocation;
import net.paoding.rose.web.impl.thread.InvocationBean;

/**
 * 仅仅提供Rose框架内部使用，外部程序请勿调用.
 * 
 * @author zhiliang.wang [qieqie.wang@paoding.net]
 */
public final class PrivateVar {

    // 存放当前线程所处理的请求对象 
    private final static ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<HttpServletRequest>();

    // 当前环境的ServletContext对象，由RoseFilter初始化时设置通过servletContext(ServlerContext)设置进来
    private static ServletContext servletContext;

    //---------------------------------------------------------------

    /**
     * 设置当前环境的 {@link ServletContext}环境对象.该设置方法在只能被调用一次.
     * <p>
     * 设置成功后，Rose框架内部的程序将调用 {@link #servletContext()}方法返回之.
     * 
     * @param servletContext
     * @throws AssertionError 如果已经成功设置过，外部程序又要重复设置时
     * @throws NullPointerException 如果所给的servletContext 为null时
     * @see #servletContext()
     */
    public static void servletContext(ServletContext servletContext) {
        // 禁止外部程序恶意调用此方法
        if (PrivateVar.servletContext != null) {
            throw new AssertionError("don't call this static method from your application code");
        }
        if (servletContext == null) {
            throw new NullPointerException("ServletContext");
        }
        PrivateVar.servletContext = servletContext;
    }

    /**
     * 返回当前环境的 {@link ServletContext}对象，在RoseFilter初始化成功后有效
     * 
     * @return
     * @throws IllegalStateException 如果还未初始化成功时
     * @see #servletContext(ServletContext)
     */
    public static ServletContext servletContext() {
        if (PrivateVar.servletContext == null) {
            throw new IllegalStateException();
        }
        return PrivateVar.servletContext;
    }

    //---------------------------------------------------------------

    /**
     * 将封装在所给{@link Invocation}中的{@link HttpServletRequest}对象绑定到当先线程中，同时把这个 {@link Invocation}
     * 作为一个Rose专有属性设置到当前的{@link HttpServletRequest}的属性中。
     * <p>
     * 通过这个设置后，后续可以通过{@link #request()}和 {@link #getInvocation()}获取到这个请求以及{@link Invocation}对象.
     * 
     * @param invocation
     * @throws NullPointerException 当所传入的invocation或invocation的request为null时
     * @see #request()
     * @see #getInvocation()
     */
    public static void setInvocation(InvocationBean invocation) {
        if (invocation == null) {
            throw new NullPointerException("invocation");
        }
        HttpServletRequest request = invocation.getRequest();
        if (request == null) {
            throw new NullPointerException("request");
        }
        PrivateVar.requestHolder.set(request);
        request.setAttribute("$$paoding-rose.invocation", invocation);
    }

    /**
     * 返回绑定到当前线程的请求对象当前时刻的 {@link Invocation}对象.
     * 
     * @throws NullPointerException 如果当前线程中没有绑定请求对象时
     */
    public static InvocationBean getInvocation() {
        HttpServletRequest request = request();
        return (InvocationBean) request.getAttribute("$$paoding-rose.invocation");
    }

    /**
     * 返回绑定到当前线程的请求对象.
     * 
     * @return
     * @throws NullPointerException 如果当前线程中没有绑定请求对象时
     */
    public static HttpServletRequest request() {
        HttpServletRequest request = (HttpServletRequest) PrivateVar.requestHolder.get();
        if (request == null) {
            throw new NullPointerException(
                    "request is not in current thread; don't call this method out of request thread");
        }
        return request;
    }

    /**
     * 将构造函数私有化，禁止实例化
     */
    private PrivateVar() {
        throw new AssertionError();
    }
}
